package com.ynu.springbootappdemo.common.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.ynu.springbootappdemo.common.exception.BusinessException;
import com.ynu.springbootappdemo.dao.IUserDao;
import com.ynu.springbootappdemo.model.entity.User;
import com.ynu.springbootappdemo.model.vo.TokenVo;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import static cn.hutool.core.lang.Console.log;
import static com.ynu.springbootappdemo.common.constant.CommonConstant.*;

@Component
@Slf4j
public class TokenUtils {
    private static IUserDao staticUserDao;

    //accessToken过期时间:20分钟
    private static final Integer ACCESS_Token_EXPIRE_TIME = 1000 * 60 * 20;

    //refreshToken过期时间:7天
    private static final Integer REFRESH_Token_EXPIRE_TIME = 1000 * 60 * 24 * 7;

    //密钥
    private static final String SECRET = "ynu.edu.lcy";

    @Resource
    private IUserDao userDao;

    @Resource
    private RedisTemplate redisTemplate;

    @PostConstruct
    public void setAdminService(){
        staticUserDao = userDao;
    }

    //生成token
    public static String createToken(User user,String tokenType){
        return JWT.create().withAudience(user.getUserPhone()) //将userid保存到token里面作为载荷
                .withIssuedAt(new Date())
                .withIssuer("lcy") //签发人
                .withClaim("role",user.getUserRole())
                .withClaim("tokenType",tokenType)
                .sign(Algorithm.HMAC256(SECRET));
    }

    /**
     * 获取Token
     * */
    public String getToken(HttpServletRequest request){
        //获取token
        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token)) {
            token = request.getParameter("token");
        }
        if (StringUtils.isEmpty(token)){
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        return token;
    }

    /**
     * 校验token合法性
     * @param token
     * @return DecodedJWT
     */
    private DecodedJWT validateToken(String token,String needTokenType){
            if (StringUtils.isBlank(token)) {
                throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
            }
            DecodedJWT decodedJWT = JWT.decode(token);
            //获取tokenType
            String tokenType = decodedJWT.getClaim("tokenType").asString();

            if (StringUtils.isBlank(tokenType) || !StringUtils.equals(tokenType,needTokenType)){
                throw new BusinessException(ErrorCode.PARAMS_ERROR);
            }
            //获取redisToken
            String userPhone = decodedJWT.getAudience().get(0);
            String redisToken = (String)redisTemplate.opsForValue().get(userPhone + tokenType);
            //判断是否过期
            if (StringUtils.isBlank(redisToken)){
                //如果accessToken过期，则获取最新token
                if (tokenType.equals(ACCESS_TOKEN)){
                    throw new BusinessException(ErrorCode.AUTH_ERROR);
                }else {
                    //如果refreshToken过期，则说明登录过期需要重新登陆
                    throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
                }
            }
            //判断是否正确合法
            if (!StringUtils.equals(token,redisToken)) {
                //token非法
                throw new BusinessException(ErrorCode.PARAMS_ERROR);
            }

//            //如果解析的是accessToken，说明用户仍处于登陆状态，则延长redis中refreshToken的过期时间
//            if (tokenType.equals(ACCESS_TOKEN)){
//                //取出refreshToken
//                String refreshToken = (String)redisTemplate.opsForValue().get(userPhone + REFRESH_TOKEN);
//                redisTemplate.opsForValue().set(userPhone + REFRESH_TOKEN,refreshToken,REFRESH_Token_EXPIRE_TIME, TimeUnit.MILLISECONDS);
//            }

            return decodedJWT;
    }

    /**
     * 通过解析token返回当前用户
     * */
    public User getCurrentUser(HttpServletRequest request,String tokenType) {
        String token = getToken(request);
        DecodedJWT decodedJWT = validateToken(token,tokenType);
        String userPhone = decodedJWT.getAudience().get(0);
        log("userPhone: " + userPhone);
        return staticUserDao.findUserByUserPhone(userPhone);
    }

    /**
     * 解析用户角色
     * */
    public String getCurrentUserRole(HttpServletRequest request, String tokenType) {
        String token = getToken(request);
        DecodedJWT decodedJWT = validateToken(token,tokenType);
        return decodedJWT.getClaim("role").asString();
    }

    /**
     * 把accessToken和refreshToken存入redis
     * */
    public void saveTokenToRedis(User user,String accessToken, String refreshToken) {
        //存入redis，过期时间为20分钟，key为userPhone+"_accessToken"
        redisTemplate.opsForValue().set(user.getUserPhone()+ACCESS_TOKEN, accessToken, ACCESS_Token_EXPIRE_TIME, TimeUnit.MILLISECONDS);
        //存入redis，过期时间为7天，key为userPhone+"_refreshToken"
        redisTemplate.opsForValue().set(user.getUserPhone()+REFRESH_TOKEN, refreshToken, REFRESH_Token_EXPIRE_TIME, TimeUnit.MILLISECONDS);
    }

    /**
     * 通过refresh_token换取新的access_token
     * */
    public TokenVo updateAccessToken(HttpServletRequest request) {
        //解析其中用户
        User user = getCurrentUser(request,REFRESH_TOKEN);
        //生成新的access_token
        String newAccessToken = createToken(user,ACCESS_TOKEN);
        //生成新的refresh_token
        String newRefreshToken = createToken(user,REFRESH_TOKEN);
        //存入redis
        saveTokenToRedis(user,newAccessToken, newRefreshToken);
        return new TokenVo(newAccessToken, newRefreshToken);
    }







}
